package com.heima.article.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.article.mapper.ApArticleMapper;
import com.heima.article.pojo.ApArticle;
import com.heima.article.pojo.ApAuthor;
import com.heima.article.service.*;
import com.heima.common.constants.message.ArticleBehaviorRedisPrefixConstants;
import com.heima.common.dtos.ArticleCommentPageDto;
import com.heima.common.enums.AppHttpCodeEnum;
import com.heima.common.exception.LeadException;
import com.heima.common.threadlocal.UserThreadLocalUtils;
import com.heima.common.util.BeanHelper;
import com.heima.common.util.JsonUtils;
import com.heima.model.article.dtos.ArticleDto;
import com.heima.model.article.dtos.ArticleHomeDto;
import com.heima.model.article.dtos.ArticleInfoDto;
import com.heima.model.article.enums.ApArticleFlagEnum;
import com.heima.model.behavior.dtos.ArticleBehaviorShowDTO;
import com.heima.model.comment.dtos.CommentPageDto;
import com.heima.model.media.dtos.WmNewsDownOrUpDTO;
import com.heima.model.media.dtos.WmNewsResultDTO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 * 文章信息表，存储已发布的文章 服务实现类
 * </p>
 *
 * @author HM
 * @since 2023-03-22
 */
@Slf4j
@Service
public class ApArticleServiceImpl extends ServiceImpl<ApArticleMapper, ApArticle> implements ApArticleService {

    @Autowired
    ApAuthorService authorService;

    @Autowired
    ApArticleContentService articleContentService;

    @Autowired
    ApFreeMarkerService freeMarkerService;

    @Autowired
    ApHotArticlesService hotArticlesService;

    @Autowired
    ApArticleMapper articleMapper;

    @Autowired
    StringRedisTemplate redisTemplate;

    //查询列表详情
    @Override
    public List<CommentPageDto> findComment(ArticleCommentPageDto dto) {
        LambdaQueryWrapper<ApArticle> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.ge(ApArticle::getCreatedTime, dto.getBeginDate());
        queryWrapper.le(ApArticle::getCreatedTime, dto.getEndDate());
        queryWrapper.orderByDesc(ApArticle::getCreatedTime);
        Page<ApArticle> ipage = new Page<>(dto.getPage(), dto.getSize());
        Page<ApArticle> page = this.page(ipage, queryWrapper);
        List<CommentPageDto> collect = page.getRecords().stream().map(apArticle -> {
            return BeanHelper.copyProperties(apArticle, CommentPageDto.class);

        }).collect(Collectors.toList());
        return collect;
    }

    /**
     * 根据文章id从redis中获取当前用户的行为数据供前端进行回显
     *
     * @param dto
     * @return
     */
    @Override
    public ArticleBehaviorShowDTO loadArticleBehavior(ArticleInfoDto dto) {
        Integer userId = UserThreadLocalUtils.getUserIdAndCheckLogin();

        // 如果UserId小于0 说明用户为未登录用户, 未登录用户不进行 行为展示数据的记录
        if (userId <= 0) {
            return new ArticleBehaviorShowDTO();
        }

        // 获取用于展示的RedisKey com:heima:behavior:show + : + {userId}
        String key = ArticleBehaviorRedisPrefixConstants.BEHAVIOR_SHOW_PREFIX + ":" + userId;

        String json = (String) redisTemplate.opsForHash().get(key, dto.getArticleId() + "");
        // 如果Json不存在, 则直接返回
        if (StringUtils.isEmpty(json)) {
            return new ArticleBehaviorShowDTO();
        }
        return JsonUtils.toBean(json, ArticleBehaviorShowDTO.class);
    }


    /**
     * 创建Article信息 使用Feign远程调用创建
     *
     * @param dto
     * @return
     */
    @Override
    public Long createArticle(WmNewsResultDTO dto) {
        log.info("开始创建Article");
        Long articleId = null;
        // 先查询是否存在article。没有则插入，有则更新
        // ap_article
        LambdaQueryWrapper<ApArticle> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ApArticle::getWmNewsId, dto.getId());
        ApArticle articleDB = this.getOne(queryWrapper);

        articleId = saveOrUpdateArticleByWmNewsResultDTO(dto, articleDB);
        // ap_article_content
        articleContentService.createArticleContent(articleId, dto);

        // 异步生成Html文件, 上传minio, 并回填地址
        freeMarkerService.asyncCreateHtmlAndUploadToMinio(dto.getContent(), articleId);
        return articleId;
    }

    /**
     * 加载app首页文章
     *
     * @param dto
     * @return
     */
    @Override
    public List<ApArticle> load(ArticleHomeDto dto) {
        return this.loadApp(dto, true);
    }

    /**
     * 加载app文章
     *
     * @param dto
     * @param isMore 是否为加载更多文章 是为true,不是(即加载更多)为false
     * @return
     */
    public List<ApArticle> loadApp(ArticleHomeDto dto, boolean isMore) {

        // 判断是不是加载热点文章, 如果是 从redis中查询
        if (dto.getChannelId() == 0) {
            return hotArticlesService.loadApp();
        }

        // 构建查询条件
        LambdaQueryWrapper<ApArticle> queryWrapper = buildLoadQueryWrapper(dto, isMore, false);
        // 构建分页
        Page<ApArticle> ipage = new Page<>(1, dto.getSize());
        return page(ipage, queryWrapper).getRecords();
    }

    /**
     * 构建App首页加载的查询条件
     *
     * @param dto
     * @param isMore 是否为加载更多文章 是为true,不是(即加载更多)为false
     * @param isHot  是否为加载热点文章(ChannelId = 0) 是为true, 不是为false
     * @return
     */
    private LambdaQueryWrapper<ApArticle> buildLoadQueryWrapper(ArticleHomeDto dto, boolean isMore, boolean isHot) {
        LambdaQueryWrapper<ApArticle> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ApArticle::getIsDown, false);
        queryWrapper.eq(ApArticle::getIsDelete, false);
        // 只有当不为查询热点信息时, 才添加的条件
        if (!isHot) {
            queryWrapper.le(isMore, ApArticle::getPublishTime, dto.getMinTime());
            queryWrapper.ge(!isMore, ApArticle::getPublishTime, dto.getMaxTime());
            queryWrapper.eq(dto.getChannelId() > 0, ApArticle::getChannelId, dto.getChannelId());
            // 构建排序条件
            queryWrapper.orderByDesc(ApArticle::getPublishTime);
        }
        return queryWrapper;
    }


    /**
     * 加载app更多文章
     *
     * @param dto
     * @return
     */
    @Override
    public List<ApArticle> loadMore(ArticleHomeDto dto) {
        return this.loadApp(dto, true);
    }

    /**
     * 加载app最新文章
     *
     * @param dto
     * @return
     */
    @Override
    public List<ApArticle> loadNew(ArticleHomeDto dto) {
        return this.loadApp(dto, false);
    }

    /**
     * 根据文章id修改文章上下架状态
     * 该方法由wmNews上下架状态的监听器调用
     *
     * @param newsDownOrUpDTO
     */
    @Override
    public void updateIsDownById(WmNewsDownOrUpDTO newsDownOrUpDTO) {
        if (newsDownOrUpDTO == null) {
            throw new LeadException(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        ApArticle apArticle = new ApArticle();
        apArticle.setId(newsDownOrUpDTO.getArticleId());
        apArticle.setIsDown(newsDownOrUpDTO.getIsDown());
        this.updateById(apArticle);

    }

    /**
     * feign调用 分页查询文章以及文章详情信息 用于全量导入es
     *
     * @param page 页码
     * @param size 页面大小
     * @return
     */
    @Override
    public List<ArticleDto> findByPage(Integer page, Integer size) {
        Page<ApArticle> ipage = new Page<>(page, size);
        return articleMapper.findByPage(ipage);
    }

    @Override
    public ArticleDto findById(Long articleId) {
        return this.baseMapper.findById(articleId);
    }


    /**
     * 查询最近 day 天发布的文章
     *
     * @param day 向之前查询的天数
     * @return
     */
    @Override
    public List<ApArticle> findLatestArticleByDays(long day) {
        // 计算时间
        LocalDateTime startTime = LocalDate.now().minusDays(day).atStartOfDay();
        LambdaQueryWrapper<ApArticle> queryWrapper = new LambdaQueryWrapper<>();

        queryWrapper.eq(ApArticle::getIsDelete, false);
        queryWrapper.eq(ApArticle::getIsDown, false);
        queryWrapper.ge(ApArticle::getPublishTime, startTime);

        return this.list(queryWrapper);
    }


    /**
     * 根据 WmNewsResultDTO 进行Article的创建和更新
     *
     * @param dto
     * @param articleDB
     * @return
     */
    private Long saveOrUpdateArticleByWmNewsResultDTO(WmNewsResultDTO dto, ApArticle articleDB) {
        Long articleId;
        ApAuthor apAuthorDB = authorService.getByWmUserId(dto.getWmUserId());
        if (articleDB == null) {
            // insert
            ApArticle insertArticle = BeanHelper.copyProperties(dto, ApArticle.class);
            // id置为空
            insertArticle.setId(null);
            insertArticle.setLayout(dto.getType());
            insertArticle.setCreatedTime(new Date());
            insertArticle.setAuthorId(apAuthorDB.getId());
            insertArticle.setAuthorName(apAuthorDB.getName());
            insertArticle.setFlag(ApArticleFlagEnum.COMMON_ARTICLE.getValue());
            insertArticle.setLikes(0L);
            insertArticle.setCollection(0L);
            insertArticle.setViews(0L);
            insertArticle.setComments(0L);
            insertArticle.setIsComment(true);
            insertArticle.setIsForward(true);
            insertArticle.setIsDown(false);
            insertArticle.setIsDelete(false);
            insertArticle.setWmNewsId(dto.getId());
            this.save(insertArticle);
            articleId = insertArticle.getId();
        } else {
            // update
            ApArticle updateArticle = BeanHelper.copyProperties(dto, ApArticle.class);
            updateArticle.setId(articleDB.getId());
            updateArticle.setLayout(dto.getType());
            updateArticle.setAuthorId(apAuthorDB.getId());
            updateArticle.setAuthorName(apAuthorDB.getName());
            updateArticle.setWmNewsId(dto.getId());
            this.updateById(updateArticle);
            articleId = articleDB.getId();
        }
        return articleId;
    }
}
